;(() => {
  if (window.__paf_injected__) return
  window.__paf_injected__ = true

  const STATE = {
    lastUser: '',
    lastPw: '',
    lastPwAt: 0,
    saveSentOnce: false,
    lastUserEl: null,
    lastPwEl: null,
    requestFillSentOnce: false
  }

  function getLowerAttr(el, name) {
    try {
      const v = (el.getAttribute && el.getAttribute(name)) || (name in el ? '' + el[name] : '')
      return (v || '').toString().trim().toLowerCase()
    } catch {
      return ''
    }
  }

  function isTextLike(el) {
    if (!el || el.nodeType !== 1 || el.tagName !== 'INPUT') return false
    const t = (el.type || '').toLowerCase()
    if (
      [
        'hidden',
        'submit',
        'button',
        'checkbox',
        'radio',
        'file',
        'number',
        'range',
        'date',
        'datetime',
        'datetime-local',
        'color'
      ].includes(t)
    )
      return false
    const ac = getLowerAttr(el, 'autocomplete')
    if (ac === 'one-time-code') return false
    return t === '' || ['text', 'email', 'tel', 'url', 'search', 'username'].includes(t)
  }

  function isPassword(el) {
    if (!el || el.nodeType !== 1 || el.tagName !== 'INPUT') return false
    const t = (el.type || '').toLowerCase()
    const ac = getLowerAttr(el, 'autocomplete')
    if (ac === 'one-time-code') return false
    return t === 'password'
  }

  function attrString(el) {
    const parts = [
      getLowerAttr(el, 'id'),
      getLowerAttr(el, 'name'),
      getLowerAttr(el, 'placeholder'),
      getLowerAttr(el, 'aria-label'),
      el.className ? String(el.className).toLowerCase() : ''
    ].filter(Boolean)
    return parts.join(' ')
  }

  function isVisible(el) {
    if (!el) return false
    if (typeof el.getClientRects === 'function') {
      const rects = el.getClientRects()
      if (rects && rects.length > 0) {
        const win = el.ownerDocument && el.ownerDocument.defaultView
        const cs = win && win.getComputedStyle ? win.getComputedStyle(el) : null
        if (
          !cs ||
          (cs.display !== 'none' &&
            cs.visibility !== 'hidden' &&
            Number.parseFloat(cs.opacity || '1') > 0)
        )
          return true
      }
    }
    return el.offsetParent !== null
  }

  // eslint-disable-next-line complexity
  function scoreUsernameField(input, passEl) {
    if (!input || !isTextLike(input)) return -Infinity
    if (typeof input.getClientRects === 'function' && input.getClientRects().length === 0)
      return -1000
    const ac = getLowerAttr(input, 'autocomplete')
    const s = attrString(input)
    let score = 0
    if (ac === 'username' || ac === 'email') score += 60
    if (s.includes('user')) score += 40
    if (s.includes('email') || s.includes('e-mail') || s.includes('mail')) score += 35
    if (
      s.includes('login') ||
      s.includes('identifier') ||
      s.includes('account') ||
      s.includes('session_key')
    )
      score += 25
    if (s.includes('phone') || s.includes('tel') || s.includes('mobile')) score += 15
    if (s.includes('customer') || s.includes('id')) score += 10
    if (s.includes('name')) score += 5
    if (s.includes('search') || s.includes('query') || s.includes('filter') || s.includes('find'))
      score -= 80
    if (s.includes('otp') || s.includes('code') || ac === 'one-time-code') score -= 100
    const t = (input.type || '').toLowerCase()
    if (t === 'email') score += 15
    if (t === 'search') score -= 20
    if (passEl && input.compareDocumentPosition) {
      const pos = input.compareDocumentPosition(passEl)
      if (pos && 4) {
        score += 10
      } else {
        score -= 5
      }
    }
    if ((input.value || '').length > 0) score += 8
    return score
  }

  function findContainer(startEl) {
    if (!startEl) return document
    if (startEl.form) return startEl.form
    if (startEl.closest) {
      const f = startEl.closest('form')
      if (f) return f
    }
    let p = startEl,
      h = 0
    while (p && h < 5) {
      if (p.querySelector && p.querySelector('input[type="password"]')) return p
      p = p.parentElement
      h++
    }
    return document
  }

  function textOfNode(el) {
    try {
      const t = (el && (el.textContent || el.textContent)) || ''
      return String(t).trim().toLowerCase()
    } catch {
      return ''
    }
  }

  function isClickable(el) {
    if (!el || el.nodeType !== 1) return false
    const tag = el.tagName
    if (tag === 'BUTTON') return true
    if (tag === 'INPUT') {
      const t = (el.type || '').toLowerCase()
      if (t === 'submit' || t === 'button' || t === 'image') return true
    }
    const r = getLowerAttr(el, 'role')
    if (r === 'button') return true
    // console.log('isClickable', tag, textOfNode(el), hasLoginKeyword(el))
    return false
  }

  function hasLoginKeyword(el) {
    const s = (attrString(el) + ' ' + textOfNode(el)).toLowerCase()
    return (
      s.includes('login') ||
      s.includes('log in') ||
      s.includes('signin') ||
      s.includes('sign in') ||
      s.includes('\u767B\u5F55') || // 登录
      s.includes('\u767B \u5F55')
    )
  }

  function isLikelyLoginTrigger(el) {
    if (!el) return false
    let n = el
    for (let i = 0; i < 3 && n; i++) {
      if (isClickable(n) && (hasLoginKeyword(n) || getLowerAttr(n, 'id').includes('login'))) {
        return true
      }
      n = n.parentElement
    }
    return false
  }

  function visibleInputs(node, selector) {
    const out = []
    const list = (node || document).querySelectorAll(selector)
    for (const el of list) {
      if (isVisible(el) && !el.disabled && !el.readOnly) out.push(el)
    }
    return out
  }

  function pickBestPassword(node) {
    const pass = visibleInputs(node, 'input[type="password"]')
    if (pass.length === 0) return null
    if (STATE.lastPwEl && pass.includes(STATE.lastPwEl)) return STATE.lastPwEl
    pass.sort(
      (a, b) =>
        (b && b.value ? String(b.value).length : 0) - (a && a.value ? String(a.value).length : 0)
    )
    return pass[0]
  }

  function pickBestUsername(node, passEl) {
    const candidates = visibleInputs(
      node,
      'input[type="text"],input[type="email"],input[type="tel"],input[type="url"],input[type="search"],input:not([type])'
    )
    let best = null,
      bestScore = -Infinity
    for (const c of candidates) {
      const sc = scoreUsernameField(c, passEl)
      if (sc > bestScore) {
        bestScore = sc
        best = c
      }
    }
    if (best) return best
    if (passEl) {
      const allInputs = visibleInputs(node, 'input')
      let candidate = null
      for (const el of allInputs) {
        if (el === passEl) break
        if (isTextLike(el)) candidate = el
      }
      if (candidate) return candidate
    }
    const hidden = (node || document).querySelectorAll('input[type="hidden"]')
    for (const el of hidden) {
      const s = attrString(el)
      if (
        (s.includes('user') ||
          s.includes('email') ||
          s.includes('login') ||
          s.includes('identifier') ||
          s.includes('account')) &&
        (el.value || '').length > 0
      )
        return el
    }
    return null
  }

  function findLoginPairAround(el) {
    const container = findContainer(el)
    const passEl = pickBestPassword(container)
    const userEl = pickBestUsername(container, passEl)
    return { userEl, passEl }
  }

  function updateStateFromPair(pair) {
    if (!pair) return
    const { userEl, passEl } = pair
    if (userEl) STATE.lastUserEl = userEl
    if (passEl) STATE.lastPwEl = passEl
    if (userEl) STATE.lastUser = userEl.value || ''
    if (passEl) {
      STATE.lastPw = passEl.value || ''
      STATE.lastPwAt = Date.now()
    }
  }

  function getSiteMeta() {
    const d = document
    const origin = location.origin
    const pageUrl = location.href
    let favicon = ''
    const link =
      d.querySelector('link[rel~="icon"]') || d.querySelector('link[rel="shortcut icon"]')
    if (link && link.getAttribute) {
      const href = link.getAttribute('href')
      if (href) {
        const u = new URL(href, d.baseURI)
        favicon = u.href
      }
    }
    const site_name = d.title || new URL(origin).hostname
    return { origin, pageUrl, favicon_url: favicon, site_name }
  }

  function sendSavePrompt() {
    if (!STATE.lastPw || STATE.saveSentOnce) return
    STATE.saveSentOnce = true
    const meta = getSiteMeta()
    const payload = {
      username: STATE.lastUser || '',
      password: STATE.lastPw || '',
      origin: meta.origin,
      pageUrl: meta.pageUrl,
      favicon_url: meta.favicon_url,
      site_name: meta.site_name
    }
    window.top?.postMessage({ __paf: true, type: 'paf:save', payload }, '*')
    console.warn('sendSavePrompt', payload)
    setTimeout(() => {
      STATE.saveSentOnce = false
    }, 8000)
  }

  function requestFill() {
    if (STATE.requestFillSentOnce) return
    STATE.requestFillSentOnce = true
    const meta = getSiteMeta()
    const payload = { origin: meta.origin, pageUrl: meta.pageUrl }
    window.top?.postMessage({ __paf: true, type: 'paf:request-fill', payload }, '*')
  }

  function hasVisiblePasswordInNode(node) {
    if (!node || !node.querySelectorAll) return false
    const list = node.querySelectorAll('input[type="password"]')
    for (const element of list) {
      if (isVisible(element)) return true
    }
    return false
  }

  function onInput(e) {
    const t = e && e.target
    if (!t) return
    if (isPassword(t) || isTextLike(t)) {
      const pair = findLoginPairAround(t)
      updateStateFromPair(pair)
    }
  }

  function onKeydown(e) {
    const t = e && e.target
    if (!t) return
    if (isPassword(t) && e && e.key === 'Enter') {
      updateStateFromPair(findLoginPairAround(t))
      sendSavePrompt()
    }
  }

  function onSubmit(_e) {
    const t = _e && _e.target
    const container = findContainer(t || document)
    let hasPw = false
    if (container) {
      hasPw = hasVisiblePasswordInNode(container)
    }
    // console.log('submit', _e, hasPw, container)
    if (hasPw) {
      updateStateFromPair(findLoginPairAround(container || document))
      sendSavePrompt()
    }
  }

  function onMessage(e) {
    const d = (e && e.data) || {}
    if (!d || d.__paf !== true || d.type !== 'paf:fill') return
    const p = d.payload || {}
    const username = p.username || ''
    const password = p.password || ''
    const pair = findLoginPairAround(document)
    if (pair && pair.userEl) {
      try {
        pair.userEl.value = username
        pair.userEl.dispatchEvent(new Event('input', { bubbles: true }))
      } catch {
        /* empty */
      }
    }
    if (pair && pair.passEl) {
      try {
        pair.passEl.value = password
        pair.passEl.dispatchEvent(new Event('input', { bubbles: true }))
      } catch {
        /* empty */
      }
    }
    updateStateFromPair(pair)
  }

  function onFocusIn(e) {
    // console.log('focusin', e)
    const t = e && e.target
    if (!t) return
    if (isPassword(t) || isTextLike(t)) {
      const pair = findLoginPairAround(t)
      // 有密码框即可请求一次自动填充（避免频繁请求）
      if (pair && pair.passEl) requestFill()
    }
  }

  function onClick(e) {
    const t = e && e.target
    if (!t) return
    // 捕获阶段尽早记录当前用户名/密码
    const pair = findLoginPairAround(t)
    updateStateFromPair(pair)
    // console.log('click', e, pair, isLikelyLoginTrigger(t))
    // 仅当附近存在密码框且点击对象疑似登录按钮时，尝试发送保存请求
    if (pair && pair.passEl && isLikelyLoginTrigger(t)) {
      // 轻微延迟，给站点脚本赋值/校验留时间，同时避免重复
      setTimeout(() => {
        updateStateFromPair(pair)
        sendSavePrompt()
      }, 50)
    }
  }

  function patchNetwork() {
    try {
      if (window.__pafNetHook__) return
      window.__pafNetHook__ = true
      // Hook fetch
      try {
        const origFetch = window.fetch
        if (typeof origFetch === 'function') {
          window.fetch = function (...args) {
            const p = origFetch.apply(this, args)
            try {
              // 统一在完成后尝试触发保存
              p.finally(() => {
                if (Date.now() - STATE.lastPwAt < 8000) sendSavePrompt()
              })
            } catch {
              p.then(
                () => {
                  if (Date.now() - STATE.lastPwAt < 8000) sendSavePrompt()
                },
                () => {
                  if (Date.now() - STATE.lastPwAt < 8000) sendSavePrompt()
                }
              )
            }
            return p
          }
        }
      } catch {
        /* empty */
      }

      // Hook XHR
      try {
        const XHR = window.XMLHttpRequest
        if (XHR && XHR.prototype) {
          const origOpen = XHR.prototype.open
          const origSend = XHR.prototype.send
          if (origOpen && origSend) {
            XHR.prototype.open = function (method, url) {
              this.__pafMethod = method
              this.__pafUrl = url

              return Reflect.apply(origOpen, this, arguments)
            }
            XHR.prototype.send = function () {
              this.addEventListener('loadend', function () {
                if (Date.now() - STATE.lastPwAt < 8000) sendSavePrompt()
              })

              return Reflect.apply(origSend, this, arguments)
            }
          }
        }
      } catch {
        /* empty */
      }
    } catch {
      /* empty */
    }
  }

  function init() {
    // 优先打补丁网络钩子，覆盖无 FORM 提交/按钮触发/SPA 请求
    patchNetwork()

    window.addEventListener('input', onInput, true)
    document.addEventListener('input', onInput, true)
    window.addEventListener('keydown', onKeydown, true)
    document.addEventListener('keydown', onKeydown, true)
    window.addEventListener('submit', onSubmit, true)
    document.addEventListener('submit', onSubmit, true)
    window.addEventListener('focusin', onFocusIn, true)
    document.addEventListener('focusin', onFocusIn, true)
    window.addEventListener('message', onMessage, true)
    window.addEventListener('click', onClick, true)
    document.addEventListener('click', onClick, true)
    window.addEventListener(
      'beforeunload',
      () => {
        if (Date.now() - STATE.lastPwAt < 5000) {
          sendSavePrompt()
        }
      },
      { capture: true }
    )
  }

  init()
})()
